package com.demo2do.mobile.web.interceptor;

import java.io.IOException;
import java.util.Date;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.util.WebUtils;

import com.demo2do.mobile.Constant;

public class WechatAuthorityInterceptor extends HandlerInterceptorAdapter {
    
    private static final Logger logger = LoggerFactory.getLogger(WechatAuthorityInterceptor.class);
    
    @Value("#{ctx['mcs.host']}")
    private String mschost;
    
    private static final String USER_AGENT = "User-Agent";
    
    private static final String MICRO_MESSENGER = "MicroMessenger";
    
    /**
     * Flags for development mock
     */
    private boolean mock = false;
    
    /**
     * WeChat openid key stored in the cookie
     */
    private String openidKey = Constant.OPENID_KEY;
    
    /**
     * User Account key stored in the cookie
     */
    private String userAccountKey = Constant.USER_ACCOUNT_KEY;
    
    /**
     * WeChat openid key stored in the cookie
     */
    private String wechatAuthUrl;
    
    /**
     * URL to display general page describes "require WeChat browser environment"
     */
    private String requireWechatUrl;
    
    /**
     * @param mock the mock to set
     */
    public void setMock(boolean mock) {
        this.mock = mock;
    }
    
    /**
     * @param openidKey the openidKey to set
     */
    public void setOpenidKey(String openidKey) {
        this.openidKey = openidKey;
    }
    
    /**
     * @param wechatAuthUrl the wechatAuthUrl to set
     */
    public void setWechatAuthUrl(String wechatAuthUrl) {
        this.wechatAuthUrl = wechatAuthUrl;
    }
    
    /**
     * @param requireWechatUrl the requireWechatUrl to set
     */
    public void setRequireWechatUrl(String requireWechatUrl) {
        this.requireWechatUrl = requireWechatUrl;
    }
    
    /*
     * (non-Javadoc)
     * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
        if (handler instanceof HandlerMethod) {
            
            if(logger.isDebugEnabled()) {
                logger.debug("WechatAuthorityInterceptor#preHandle - To determine this request can be visited in addition to wechat client");
            }
            
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            WechatAuthority wechatAuthority = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), WechatAuthority.class);
            
            // Return super prehanler 
            if(wechatAuthority == null) {
                logger.info("WechatAuthorityInterceptor#preHandle - wechatAuthority interceptor is null.");
                return super.preHandle(request, response, handler);
            }
            
            // Start WechatAuthorityInterceptor
            logger.info("WechatAuthorityInterceptor#preHandle - start check wechatAuthority...");
            
            // 1. Generate requiredUrl
            String requiredUrl = StringUtils.isBlank(wechatAuthority.targetUrl()) ? ( this.requireWechatUrl == null ? "" : this.requireWechatUrl ) : wechatAuthority.targetUrl();
            
            // 2. Get user agent
            // FIXME is userAgent is null return mobile global error pages;
            String userAgent = request.getHeader(USER_AGENT);
            logger.info("WechatAuthorityInterceptor#preHandler() - Get user agent [{}].", userAgent);
            
            
            boolean isMicroMessenger = StringUtils.isNotBlank(userAgent) && userAgent.indexOf(MICRO_MESSENGER) >= 0;
            
            // 3. go to requiredUrl, when request not comes from a wechat client.
            if(!isMicroMessenger && !this.mock) {
                
                String resolvedTargetUrl = requiredUrl.startsWith("http") ? requiredUrl : (com.demo2do.core.web.utils.WebUtils.getRealServerPath(request) + requiredUrl);
                logger.info("WechatAuthorityInterceptor#preHandle - go to resolvedTargetUrl [{}], current user agent is not microMessenger.", resolvedTargetUrl);
                
                response.sendRedirect(resolvedTargetUrl);
                return false;
            }
            
            // 4. check need wechat authority or not, when request comes from a wechat client.
            if(isMicroMessenger || this.mock) {
                
                logger.info("WechatAuthorityInterceptor#preHandle - check need wechat authority or not, when request comes from a wechat client.");
                
                // need wechat authority
                if(this.needWechatAuthority(openidKey, userAccountKey, request)) {
                    
                    logger.info("WechatAuthorityInterceptor#preHandle - need wechat authority");
                    this.doWechatAuthority(request, response);
                    return false;
                }
            }
        }
        
        return super.preHandle(request, response, handler);
    }
    
    /**
     * Save user mobile into cookie by wechat openid
     * 
     * @param request
     * @param response
     * @param wechatClient
     * @return
     */
    private boolean needWechatAuthority(String openidKey, String userAccountKey, HttpServletRequest request) {
        
        // Start check
        logger.info("WechatAuthorityInterceptor#needWechatAuthority - start check needWechatAuthority or not");
        
        // Get cookie by key
        Cookie openidCookie = WebUtils.getCookie(request, openidKey);
        Cookie userAcccountCookie = WebUtils.getCookie(request, userAccountKey);
        
        // Cookie is null
        if (openidCookie == null || userAcccountCookie == null) {
            logger.info("WechatAuthorityInterceptor#needWechatAuthority - get null value of openid or wechatAccount from cookie with openidKey [{}] userAccount key [{}]", openidKey, userAccountKey);
            return true;
        }
        
        // Cookie exit
        logger.info("WechatAuthorityInterceptor#needWechatAuthority - get openid [{}]  userAccount [{}] from cookie with openidKey [{}] and userAccountKey [{}]", openidCookie.getValue(), userAcccountCookie.getValue(), openidKey, userAccountKey);
        return false;
    }

    /**
     * Do wechat authority
     * 
     * @param request
     * @param response
     * @throws IOException
     */
    private void doWechatAuthority(HttpServletRequest request, HttpServletResponse response) throws IOException {
        
        String resolvedAuthUrl = this.generateAuthPrepare(request);
        logger.trace("WechatAuthorityInterceptor#doWechatAuthority() - The resolved wechat auth URL is [{}], goto do wechat authority.", resolvedAuthUrl);
        
        response.sendRedirect(resolvedAuthUrl);
    }
    
    /**
     * Save original request in HTTP session for further reference
     * 
     * @param request
     */
    private String generateAuthPrepare(HttpServletRequest request) {
        
        String mscPrepareUrl = this.wechatAuthUrl.startsWith("http") ? 
                               wechatAuthUrl : (mschost + wechatAuthUrl);
        
        // Construct original URL from request URI and query string
        String client = com.demo2do.core.web.utils.WebUtils.getRealServerPath(request);
        
        String originalUri = com.demo2do.core.web.utils.WebUtils.getRealRequestURI(request);
        String queryString = request.getQueryString();
        String redirectUrl = client + (StringUtils.isBlank(queryString) ? originalUri : (originalUri + "?" + queryString));
        
        String state = String.valueOf(new Date().getTime());
        
        return mscPrepareUrl + "&redirectUrl=" + redirectUrl
                             + "&client=" + client
                             + "&requestNo=" + state;
    }
}
